package com.simpleim.starter.client;

import com.simpleim.common.task.FutureTaskScheduler;
import com.simpleim.im.common.bean.User;
import com.simpleim.starter.command.*;
import com.simpleim.starter.sender.ChatSender;
import com.simpleim.starter.sender.LoginSender;
import io.netty.channel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

/**
 * 总集
 */
@Service
public class CommandController {

    private static final Logger LOG = LoggerFactory.getLogger(CommandController.class);

    @Autowired
    private LoginCommand loginCommand;

    @Autowired
    private ChatCommand chatCommand;

    @Autowired
    private MenuCommand menuCommand;

    @Autowired
    private LogoutCommand logoutCommand;

    private Map<String, BaseCommand> allCommand;

    private boolean connectedFlag = false;

    private ClientSession session;

    @Autowired
    private ChatNettyClient nettyClient;

    private User user;

    @Autowired
    private LoginSender loginSender;

    @Autowired
    private ChatSender chatSender;

    ChannelFutureListener connectedListener = new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (future.isSuccess()) {
                // 连接成功
                session = new ClientSession(future.channel());
                connectedFlag = true;
                session.setConnected(true);
                // 关闭监听器
                future.channel().closeFuture().addListener(closeListener);
                notifyCommandThread();
            } else {
                // 连接失败，每10秒重连一次
                final EventLoop loop = future.channel().eventLoop();
                loop.schedule(() -> {
                    // 重连
                    nettyClient.doConnect();
                }, 10, TimeUnit.SECONDS);
            }
        }
    };

    ChannelFutureListener closeListener = new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (future.isSuccess()) {
                LOG.info(new Date() + ": 连接已经断开……");
                connectedFlag = false;
                ClientSession.closeSession(future.channel());
                notifyCommandThread();
            }
        }
    };

    public void initCommands() {
        allCommand = new HashMap<>();
        allCommand.put(loginCommand.type(), loginCommand);
        allCommand.put(chatCommand.type(), chatCommand);
        allCommand.put(menuCommand.type(), menuCommand);
        allCommand.put(logoutCommand.type(), logoutCommand);
        menuCommand.setAllCommand(allCommand);
    }

    /**
     * 连接服务器
     */
    public void commandThreadRunning() {
        Thread.currentThread().setName("命令线程");

        while (true) {
            while (!connectedFlag) {
                startConnectServer();
                waitCommandThread();
            }
            /**
             * session不为空，表示已经连接上了
             */
            while (null != session) {
                // 输入命令
                Scanner scanner = new Scanner(System.in);
                menuCommand.exec(scanner);
                String key = menuCommand.getCommandInput();
                BaseCommand command = allCommand.get(key);
                if (command == null) {
                    LOG.info("无法识别[" + command + "]指令，请重新输入!");
                    continue;
                }
                switch (key) {
                    case LoginCommand.KEY:
                        LoginCommand loginCommand = (LoginCommand) command;
                        loginCommand.exec(scanner);
                        doLogin(loginCommand);
                        break;
                    case ChatCommand.KEY:
                        ChatCommand chatCommand = (ChatCommand) command;
                        chatCommand.exec(scanner);
                        doChat(chatCommand);
                        break;
                    case LogoutCommand.KEY:
                        LogoutCommand logoutCommand = (LogoutCommand) command;
                        logoutCommand.exec(scanner);
                        doLogout(logoutCommand);
                        break;
                    default:
                        break;
                }
            }

        }

    }

    public void doLogin(LoginCommand command) {
        //登录
        if (!isConnectedFlag()) {
            LOG.info("连接异常，请重新建立连接");
            return;
        }
        User user = new User();
        user.setUid(command.getUsername());
        user.setToken(command.getPassword());
        user.setDevId("11111");
        user.setNickName(command.getUsername());

        this.user = user;
        session.setUser(user);

        // ChatServer执行登录操作
        loginSender.setSession(session);
        loginSender.setUser(user);
        loginSender.sendLoginMsg();
    }

    public void doChat(ChatCommand command) {
        // 执行登录
        chatSender.setSession(session);
        chatSender.setUser(user);
        chatSender.sendChatMsg(command.getUserId(), command.getMessage());
    }

    public void doLogout(LogoutCommand command) {
        // 进行登出操作
    }

    /**
     * 等待线程
     */
    public void waitCommandThread() {
        synchronized (this) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                LOG.error(e.getMessage());
            }
        }
    }

    /**
     * 唤醒用户线程
     */
    public void notifyCommandThread() {
        synchronized (this) {
            this.notify();
        }
    }

    public void startConnectServer() {
        FutureTaskScheduler.add(() -> {
            nettyClient.setListener(connectedListener);
            nettyClient.doConnect();
        });
    }

    public void setConnectedFlag(boolean connectedFlag) {
        this.connectedFlag = connectedFlag;
    }

    public boolean isConnectedFlag() {
        return connectedFlag;
    }
}
